#include "StdAfx.h"
#include "Window.h"
#include "WindowsX.h"

#ifdef _DEBUG
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#endif

void __Trace(LPCTSTR pstrFormat, ...)
{
#ifdef _DEBUG
   TCHAR szBuffer[300] = { 0 };
   va_list args;
   va_start(args, pstrFormat);
   ::wvnsprintf(szBuffer, lengthof(szBuffer) - 2, pstrFormat, args);
   _tcscat(szBuffer, _T("\n"));
   va_end(args);
   ::OutputDebugString(szBuffer);
#endif
}

/////////////////////////////////////////////////////////////////////////////////////////////////////


CWindow::CWindow() : m_hWnd(NULL), m_OldWndProc(::DefWindowProc), m_bSubclassed(false)
{
}

CWindow::~CWindow()
{
}

HWND CWindow::GetHWND() const 
{ 
   return m_hWnd; 
}

UINT CWindow::GetClassStyle() const
{
   return (CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS);
}

CWindow::operator HWND() const
{
   return m_hWnd;
}

HWND CWindow::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, const RECT rc, HMENU hMenu)
{
   return Create(hwndParent, pstrName, dwStyle, dwExStyle, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hMenu);
}

HWND CWindow::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
   HINSTANCE hInstance = GetModuleHandle(NULL);
   if ( NULL == hInstance ) return NULL;

   if( !RegisterWindowClass() ) return NULL;
   m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, hInstance, this);
   ASSERT(m_hWnd!=NULL);
   return m_hWnd;
}

HWND CWindow::Subclass(HWND hWnd)
{
   ASSERT(::IsWindow(hWnd));
   ASSERT(m_hWnd==NULL);
   m_OldWndProc = SubclassWindow(hWnd, __WndProc);
   if( m_OldWndProc == NULL ) return NULL;
   ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(this));
   m_bSubclassed = true;
   m_hWnd = hWnd;
   return m_hWnd;
}

void CWindow::Unsubclass()
{
   ASSERT(::IsWindow(m_hWnd));
   if( !::IsWindow(m_hWnd) ) return;
   if( !m_bSubclassed ) return;
   SubclassWindow(m_hWnd, m_OldWndProc);
   m_OldWndProc = ::DefWindowProc;
   m_bSubclassed = false;
}

void CWindow::ShowWindow(bool bShow)
{
   ASSERT(::IsWindow(m_hWnd));
   if( !::IsWindow(m_hWnd) ) return;
   ::ShowWindow(m_hWnd, bShow ?  SW_SHOWNORMAL : SW_HIDE);
}

void CWindow::Close()
{
   ASSERT(::IsWindow(m_hWnd));
   if( !::IsWindow(m_hWnd) ) return;
   PostMessage(WM_CLOSE);
}

void CWindow::CenterWindow()
{
   ASSERT(::IsWindow(m_hWnd));
   ASSERT((GetWindowStyle(m_hWnd)&WS_CHILD)==0);
   RECT rcDlg = { 0 };
   ::GetWindowRect(m_hWnd, &rcDlg);
   RECT rcArea = { 0 };
   RECT rcCenter = { 0 };
   HWND hWndParent = ::GetParent(m_hWnd);
   HWND hWndCenter = ::GetWindowOwner(m_hWnd);
   ::SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);
   if( hWndCenter == NULL ) rcCenter = rcArea; else ::GetWindowRect(hWndCenter, &rcCenter);

   int DlgWidth = rcDlg.right - rcDlg.left;
   int DlgHeight = rcDlg.bottom - rcDlg.top;

   int xLeft = (rcCenter.left + rcCenter.right) / 2 - DlgWidth / 2;
   int yTop = (rcCenter.top + rcCenter.bottom) / 2 - DlgHeight / 2;

   if( xLeft < rcArea.left ) xLeft = rcArea.left;
   else if( xLeft + DlgWidth > rcArea.right ) xLeft = rcArea.right - DlgWidth;
   if( yTop < rcArea.top ) yTop = rcArea.top;
   else if( yTop + DlgHeight > rcArea.bottom ) yTop = rcArea.bottom - DlgHeight;
   ::SetWindowPos(m_hWnd, NULL, xLeft, yTop, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}

void CWindow::SetIcon(UINT nRes)
{
   HINSTANCE hInstance = GetModuleHandle(NULL);
   if ( NULL == hInstance ) return;

   HICON hIcon = (HICON)::LoadImage(hInstance, MAKEINTRESOURCE(nRes), IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
   ASSERT(hIcon);
   ::SendMessage(m_hWnd, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon);
   hIcon = (HICON)::LoadImage(hInstance, MAKEINTRESOURCE(nRes), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
   ASSERT(hIcon);
   ::SendMessage(m_hWnd, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon);
}

bool CWindow::RegisterWindowClass()
{
   HINSTANCE hInstance = GetModuleHandle(NULL);
   if ( NULL == hInstance ) return false;

   WNDCLASS wc = { 0 };
   wc.style = GetClassStyle();
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hIcon = NULL;
   wc.lpfnWndProc = CWindow::__WndProc;
   wc.hInstance = hInstance;
   wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = NULL;
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = GetWindowClassName();
   ATOM ret = ::RegisterClass(&wc);
   ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS);
   return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
}

LRESULT CALLBACK CWindow::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
   CWindow* pThis = NULL;
   if( uMsg == WM_NCCREATE ) {
      LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
      pThis = static_cast<CWindow*>(lpcs->lpCreateParams);
      pThis->m_hWnd = hWnd;
      ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
   } 
   else {
      pThis = reinterpret_cast<CWindow*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
      if( uMsg == WM_NCDESTROY && pThis != NULL ) {
         LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);
         ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);
         if( pThis->m_bSubclassed ) pThis->Unsubclass();
         pThis->m_hWnd = NULL;
         pThis->OnFinalMessage(hWnd);
         return lRes;
      }
   }
   if( pThis != NULL ) {
      return pThis->HandleMessage(uMsg, wParam, lParam);
   } 
   else {
      return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
   }
}

LRESULT CWindow::SendMessage(UINT uMsg, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/)
{
   ASSERT(::IsWindow(m_hWnd));
   return ::SendMessage(m_hWnd, uMsg, wParam, lParam);
} 

LRESULT CWindow::PostMessage(UINT uMsg, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/)
{
   ASSERT(::IsWindow(m_hWnd));
   return ::PostMessage(m_hWnd, uMsg, wParam, lParam);
}

LRESULT CWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
   return ::CallWindowProc(m_OldWndProc, m_hWnd, uMsg, wParam, lParam);
}

void CWindow::OnFinalMessage(HWND hWnd)
{
}